package com.xb.ai.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ReUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xb.ai.constant.ErrorMessage;
import com.xb.ai.entity.po.User;
import com.xb.ai.entity.request.UserRegisterRequest;
import com.xb.ai.entity.vo.UserVo;
import com.xb.ai.exception.BusinessException;
import com.xb.ai.service.IUserService;
import com.xb.ai.mapper.UserMapper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
        implements IUserService {

    private static final String SALT = "xbAI";
    private static final long LOGIN_DELAY_MS = 200;
    private static final int MAX_IP_LOGIN_ATTEMPTS = 5;
    private static final String LOGIN_IP_ATTEMPTS_PREFIX = "login:ip_attempts:";
    private static final String LOGIN_IP_BLOCKED_PREFIX = "login:ip_block:";

    private final BloomFilterService bloomFilterService;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 获取当前请求的 IP 地址
     */
    private String getCurrentRequestIp() {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            String ip = request.getHeader("X-Forwarded-For");
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            // 对于多个代理的情况，第一个IP为客户端真实IP
            if (ip != null && ip.contains(",")) {
                ip = ip.split(",")[0].trim();
            }
            return ip;
        }
        return "unknown_ip"; // 或者抛出异常，表示无法获取请求上下文
    }

    /**
     * 计算到当天午夜的秒数
     */
    private long getSecondsUntilMidnight() {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime midnight = LocalDateTime.of(now.toLocalDate(), LocalTime.MAX);
        return Duration.between(now, midnight).getSeconds();
    }

    /**
     * 用户登录
     */
    @Override
    public UserVo login(String userAccount, String userPassword) {
        String clientIp = getCurrentRequestIp();
        String ipBlockKey = LOGIN_IP_BLOCKED_PREFIX + clientIp;
        String ipAttemptsKey = LOGIN_IP_ATTEMPTS_PREFIX + clientIp;

        // 1. 检查 IP 是否被锁定
        if (Boolean.TRUE.equals(redisTemplate.hasKey(ipBlockKey))) {
            throw new BusinessException(ErrorMessage.OPERATION_ERROR + ": 登录失败次数过多，请明天再试");
        }

        // 统一处理登录失败逻辑
        Runnable handleLoginFailure = () -> {
            Long attempts = redisTemplate.opsForValue().increment(ipAttemptsKey);
            redisTemplate.expire(ipAttemptsKey, getSecondsUntilMidnight() + 1, TimeUnit.SECONDS);
            if (attempts != null && attempts >= MAX_IP_LOGIN_ATTEMPTS) {
                redisTemplate.opsForValue().set(ipBlockKey, "blocked", getSecondsUntilMidnight(), TimeUnit.SECONDS);
                redisTemplate.delete(ipAttemptsKey);
                throw new BusinessException(ErrorMessage.OPERATION_ERROR + ": 登录失败次数过多，请明天再试");
            }
            throw new BusinessException(ErrorMessage.USER_LOGIN_ERROR);
        };

        // 2. 参数校验
        if (!ReUtil.isMatch("^[a-zA-Z]{1}[a-zA-Z0-9]{5,12}", userAccount)) {
            handleLoginFailure.run();
        }
        if (!ReUtil.isMatch("^[a-zA-Z0-9]{6,12}", userPassword)) {
            handleLoginFailure.run();
        }

        // 3. 登录延迟 防止被攻击
        try {
            Thread.sleep(LOGIN_DELAY_MS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        // 4. 使用布隆过滤器初步判断用户名是否存在
        synchronized (userAccount.intern()) {
            if (!bloomFilterService.mightContain(userAccount)) {
                handleLoginFailure.run();
            }

            // 5. 查询数据库
            User user = lambdaQuery()
                    .select(User::getId, User::getUserPassword, User::getUserRole, User::getCreateTime)
                    .eq(User::getUserAccount, userAccount)
                    .one();

            // 6. 判断用户是否存在及密码是否正确
            if (user == null) {
                handleLoginFailure.run();
            }

            String encryptedPassword = DigestUtils.md5DigestAsHex((userPassword + SALT).getBytes());
            if (Optional.ofNullable(user).map(User::getUserPassword).isPresent() && !encryptedPassword.equals(user.getUserPassword())){
                handleLoginFailure.run();
            }

            // 7. 登录成功
            StpUtil.login(user.getId());
            // 登录成功后，清除该IP的登录失败次数记录
            redisTemplate.delete(ipAttemptsKey);
            return BeanUtil.copyProperties(user, UserVo.class);
        }
    }

    /**
     * 用户注册
     */
    @Override
    public void register(UserRegisterRequest userRegisterRequest) {
        String userAccount = userRegisterRequest.getUserAccount();
        String userPassword = userRegisterRequest.getUserPassword();
        String checkPassword = userRegisterRequest.getCheckPassword();

        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) {
            throw new BusinessException(ErrorMessage.PARAMS_ERROR + ": 注册信息不能为空");
        }
        if (!ReUtil.isMatch("^[a-zA-Z]{1}[a-zA-Z0-9]{5,12}", userAccount)) {
            throw new BusinessException(ErrorMessage.USER_ACCOUNT_INCORRECT_FORMAT);
        }
        if (!ReUtil.isMatch("^[a-zA-Z0-9]{6,12}", userPassword)) {
            throw new BusinessException(ErrorMessage.USER_PASSWORD_INCORRECT_FORMAT);
        }
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorMessage.PARAMS_ERROR + ": 两次输入的密码不一致");
        }
        synchronized (userAccount.intern()) {
            if (bloomFilterService.mightContain(userAccount)) {
                User user = lambdaQuery()
                        .select(User::getUserAccount)
                        .one();
                if (user != null) {
                    throw new BusinessException(ErrorMessage.OPERATION_ERROR + ": 该账号已被注册");
                }
            }
            String encryptedPassword = DigestUtils.md5DigestAsHex((userPassword + SALT).getBytes());

            User newUser = new User();
            newUser.setUserAccount(userAccount);
            newUser.setUserPassword(encryptedPassword);

            boolean saveResult = this.save(newUser);
            if (!saveResult) {
                throw new BusinessException(ErrorMessage.SYSTEM_ERROR + ": 注册失败，数据库操作异常");
            }

            bloomFilterService.put(userAccount);
        }
    }
}




